package 反射.代理;

import java.lang.reflect.Proxy;
import java.util.Arrays;

/*
JDK动态代理有以下特点: 
	1.代理对象,不需要实现接口  目标对象还是要实现接口
	2.代理对象的生成,是利用JDK的API,动态的在内存中构建代理对象(需要我们指定创建代理对象/目标对象实现的接口的类型)
	3.动态代理也叫做:JDK代理,接口代理

	JDK中生成代理对象的API
	  代理类所在包:java.lang.reflect.Proxy
	  JDK实现代理只需要使用newProxyInstance方法,但是该方法需要接收三个参数,完整的写法是: 
	  static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces,InvocationHandler h ) 
	注意该方法是在Proxy类中是静态方法,且接收的三个参数依次为:
	  
	  ClassLoader loader:指定当前目标对象使用类加载器,获取加载器的方法是固定的 
	  Class<?>[] interfaces:目标对象实现的接口的类型,使用泛型方式确认类型 
	  InvocationHandler h:事件处理,执行目标对象的方法时,会触发事件处理器的方法,会把当前执行目标对象的方法作为参数传入
我们来比较Java的class和interface的区别：

可以实例化class（非abstract）；不能实例化interface。
所有interface类型的变量总是通过向上转型并指向某个实例的：
CharSequence cs = new StringBuilder();
有没有可能不编写实现类，直接在运行期创建某个interface的实例呢？
这是可能的，因为Java标准库提供了一种动态代理（Dynamic Proxy）的机制：可以在运行期动态创建某个interface的实例。
什么叫运行期动态创建？听起来好像很复杂。所谓动态代理，是和静态相对应的。我们来看静态代码怎么写：

定义接口：

public interface Hello {
    void morning(String name);
}
编写实现类：
public class HelloWorld implements Hello {
    public void morning(String name) {
        System.out.println("Good morning, " + name);
    }
}
创建实例，转型为接口并调用：
Hello hello = new HelloWorld();
hello.morning("Bob");
这种方式就是我们通常编写代码的方式。

还有一种方式是动态代码，我们仍然先定义了接口Hello，但是我们并不去编写实现类，而是直接通过JDK提供的一个Proxy.newProxyInstance()创
建了一个Hello接口对象。
这种没有实现类但是在运行期动态创建了一个接口对象的方式，我们称为动态代码。JDK提供的动态创建接口对象的方式，就叫动态代理。
一个最简单的动态代理实现如下：
*/
/*
import java.lang.reflect.InvocationHandler;
//Invocation:祈祷，求助，调用(计算机用语)
import java.lang.reflect.Method;
public class 动态代理 {
    public static void main(String[] args) {
        InvocationHandler handler = new InvocationHandler() {
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                System.out.println("method: "+method);
                if (method.getName().equals("morning")) {
                    System.out.println("Good morning, " + args[0]);
                }
                return null;//调用的方法无返回，即norning方法无返回
            }
        };
        //方法一：1.主程序生成InvocationHandler实例，改写invoke方法；2.直接通过Proxy.newProxyInstance获取代理类实例
        //Hello hello = (Hello) Proxy.newProxyInstance(
        //    Hello.class.getClassLoader(), // 传入ClassLoader
        //    new Class[] { Hello.class }, // 传入要实现的接口
        //    handler); // 传入处理调用方法的InvocationHandler
        //hello.morning("Bob");
       //方法二：1.主程序生成InvocationHandler实例，并改写invoke方法；2.代理类：实现接口；持有InvocationHandler实例。
   	 HelloDynamicProxy hello=new HelloDynamicProxy(handler);
   	 hello.morning("Bob");
    }
}

class HelloDynamicProxy implements Hello {//方法二：代理类实现接口；持有InvocationHandler实例。
	InvocationHandler handler;
	public HelloDynamicProxy(InvocationHandler handler) {
		this.handler = handler;
	}
	@Override
	public void morning(String name){
			try {
				handler.invoke(this,Hello.class.getMethod("morning", String.class),new String[] { name });
			} catch (NoSuchMethodException e) {
				e.printStackTrace();
			} catch (SecurityException e) {
				e.printStackTrace();
			} catch (Throwable e) {
				e.printStackTrace();
			} 
	}
}
interface Hello {
    void morning(String name);
}
*/
/*
在运行期动态创建一个interface实例的（JDK方法）方法如下：
方法一、直接实现接口
• 定义一个InvocationHandler实例，并改写invoke方法，它负责实现接口的方法调用；
  改写invoke方法，三个参数：Object proxy, Method method, Object[] args。完成代理对象的实际功能。
• 通过Proxy.newProxyInstance()创建interface实例，它需要3个参数：
   1.使用的ClassLoader，通常就是接口类的ClassLoader；如:Hello.class.getClassLoader()
   2.需要实现的接口数组，至少需要传入一个接口进去；如：new Class[]{Hello.class}
   3.用来处理接口方法调用的InvocationHandler实例。也即前面定义的InvocationHandler实例。如：handler
• 将返回的Object强制转型为接口。如：Hello hello=(Hello)Proxy.newProxyInstance(Hello.class.getClassLoader(),new Class[]{Hello.class},handler);

方法二、仿静态代理方法：（代理类实现代理接口，传入InvocationHandler实例，通过代理类实例调用改写的接口方法。）
• 定义一个InvocationHandler实例，并改写invoke方法，它负责实现接口的方法调用；
  改写invoke方法，三个参数：Object proxy, Method method, Object[] args。完成代理对象的实际功能。
• 定义实现接口的代理类。
  1.代理类的属性为InvocationHandler实例，构造函数参数为InvocationHandler实例。如：InvocationHandler handler;public HelloDynamicProxy(InvocationHandler handler) {
		this.handler = handler;}
  2.实现代理的接口。如:Hello
  3.通过InvocationHandler实例方法invoke调用，改写代理接口方法，如：handler.invoke(this,Hello.class.getMethod("morning", String.class),new String[] { name });
• 通过代理类实例，调用接口方法。如：HelloDynamicProxy hello=new HelloDynamicProxy(handler);hello.morning("Bob");

方法三、仿静态代理方法：（目标类实现代理接口，代理类实现InvocationHandler接口，通过代理类实例调用改写的接口方法，代理类持有目标类实例。）
• 设置接口实现类，也即代理目标类。如：
• 设置代理类，构造函数参数为代理目标类实例(targetObject)，。
  1.方法参数：Object proxy, Method method, Object[] args
  2.通过调用代理类实例的invoke方法，实现代理类功能。如：method.invoke(targetObject,args)
• 通过Proxy.newProxyInstance()创建interface实例，Proxy.newProxyInstance(targetObject.getClass().getClassLoader(),
				targetObject.getClass().getInterfaces(), proxy);
	其中targetObject为传入实际参数的实例，proxy为实现实现InvocationHandler接口invoke方法的目标类实例。

*/
//方法三：
/*
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

import lombok.Data;

interface UserBean1 {
	String getUser();
}

@Data
class UserBeanImpl1 implements UserBean1 {
	private String user = null;
	private String flag = null;// flag:0 无权限，1有权限。

	public UserBeanImpl1(String user, String flag) {
		this.user = user;
		this.flag = flag;
	}
	public String getFlag() {
		return flag;
	}
	public void setFlag(String flag) {
		this.flag = flag;
	}
	public String getUserName() {
		return user;
	}
	public String getUser() {
		System.out.println("this is getUser() method!");
		return user;
	}
	public void setUser(String user) {
		this.user = user;
		System.out.println("this is setUser() method!");
	}
}

class UserBeanProxy implements InvocationHandler {
	private Object targetObject;//接口的实现类，也即代理目标类

	public UserBeanProxy(Object targetObject) {
		this.targetObject = targetObject;
	}

	@Override
	public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
		UserBeanImpl1 userBean1 = (UserBeanImpl1) targetObject;//强制转型为代理目标类
		
		String flag = userBean1.getFlag();
		Object result = null;
		System.out.println(method.getName());
		// 权限判断
		if ("1".equals(flag)) {
			result = method.invoke(targetObject, args);
		} else {
			System.out.println("sorry , You don't have permission");
		}
		return result;
	}
}

public class JDK动态代理 {
	public static void main(String[] args) {
		UserBeanImpl1 targetObject = new UserBeanImpl1("蕾蕾", "1");
		UserBeanProxy proxy = new UserBeanProxy(targetObject);
		// 生成代理对象
		UserBean1 object = (UserBean1) Proxy.newProxyInstance(targetObject.getClass().getClassLoader(),
				targetObject.getClass().getInterfaces(), proxy);

		String userName = object.getUser();
		System.out.println("userName: " + userName);
	}
}
*/

/*
方法四：接口、接口实现类、代理工厂类：ProxyFactory.java 
	  接口类IUserDao.java以及接口实现类,目标对象UserDao是一样的,没有做修改.在这个基础上,增加一个代理工厂类(ProxyFactory.java),将代理类
	  写在这个地方,然后在测试类(需要使用到代理的代码)中先建立目标对象和代理对象的联系,然后代用代理对象的中同名方法
	 
	  代理工厂类:持有目标类实例，构造函数参数为目标类实例，类方法返回代理类实例：Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(),
				new InvocationHandler() {//此处改写invoke方法
				在Proxy.newProxyInstance第三个参数(InvocationHandler实例)处改写invoke方法。
	  传入代理目标实例，实现代理工厂实例，调用方法。
*/
/*
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
interface IUserDao {
	void save();
	
}

//目标对象:UserDao.java
class UserDao implements IUserDao {
	public void save() {// 目标对象需要静态实现接口
		System.out.println("----已经保存数据!----");
	}
}

class ProxyFactory {// 不需要静态实现接口
	private Object target;// 维护一个目标对象

	public ProxyFactory(Object target) {
		this.target = target;
	}

	// 给目标对象生成代理对象
	public Object getProxyInstance() {
		return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(),
				new InvocationHandler() {
					@Override
					public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
						System.out.println("开始事务2");
						// 执行目标对象方法
						Object returnValue = method.invoke(target, args);
						System.out.println("提交事务2");
						return returnValue;
					}
				});
	}
}

public class JDK动态代理 {
	public static void main(String[] args) {
		// 接口对象指向目标对象(向上转型)
		IUserDao target = new UserDao();
		System.out.println(target.getClass());// 原始的类型(目标对象): class 反射.UserDao
		System.out.println(Arrays.toString(target.getClass().getInterfaces()));// 目标对象的接口类型：[interface 反射.IUserDao]
		// 给目标对象，创建代理对象:
		IUserDao proxy = (IUserDao) new ProxyFactory(target).getProxyInstance();
		System.out.println(proxy.getClass());// 内存中动态生成的代理对象:class 反射.$Proxy0

		// 执行方法 【代理对象】
		proxy.save();
	}
}
*/
/*方法五：与方法四类似，但思路比较清晰！
在代理类中实现InvocationHandler接口，设置方法Object bind(),绑定委托对象，并返回代理类
即代理类与代理目标类绑定，又实现InvocationHandler接口，并改写invoke方法。
*/
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
interface Subject {//接口
	public void doSomething();
	public void doSomething1();
}

class RealSubject implements Subject {//被代理类
	public void doSomething() {
		System.out.println("call doSomething()");
	}
	public void doSomething1() {
		System.out.println("call doSomething1()");
	}
}

class ProxyHandler implements InvocationHandler {//代理类
	private Object tar;

	// 绑定委托对象（代理目标类），并返回代理类
	public Object bind(Object tar) {
		this.tar = tar;
		// 绑定该类实现的所有接口，取得代理类
		return Proxy.newProxyInstance(tar.getClass().getClassLoader(), tar.getClass().getInterfaces(), this);
	}
	
	@Override
	public Object invoke(Object proxy, Method method, Object[] args) throws Throwable{// 不依赖具体接口实现
		Object result = null;// 被代理的类型为Object基类 -->其他类可以转型
		// 这里就可以进行所谓的AOP编程了
		// 在调用具体函数方法前，执行功能处理
		System.out.println(method.getName());
		result = method.invoke(tar, args);
		// 在调用具体函数方法后，执行功能处理
		return result;
	}
}

public class JDK动态代理 {
	public static void main(String args[]) {
		ProxyHandler proxy = new ProxyHandler();
		// 绑定该类实现的所有接口
		Subject sub = (Subject) proxy.bind(new RealSubject());
		sub.doSomething();// 被invoke截获执行
		sub.doSomething1();// 被invoke截获执行
	}
}

//在调用过程中使用了通用的代理类包装了RealSubject实例，然后调用了Jdk的代理工厂方法实例化了一个具体的代理类。最后调用代理的doSomething方法，
//还有附加的before、after方法可以被任意复用（只要我们在调用代码处使用这个通用代理类去包装任意想要需要包装的被代理类即可）。当接口改变的时候，
//虽然被代理类需要改变，但是我们的代理类却不用改变了。这个调用虽然足够灵活，可以动态生成一个具体的代理类，而不用自己显示的创建一个实现具体接口的代理类。

